#include "I2C.h"


#define SLEEP_INTERVAL 1 //i2c defines scl as 100kHz max(10us period)

I2C::I2C(tvPTR *DATA, tvPTR *DIR, char pdatabit, char pclockbit)
    :IO (DATA, DIR) //parent class initialization
{
    perror("I2C::I2C()");
    printf("\t SDA: %X SCL: %X DATA: %X, DIR %X\n", pdatabit, pclockbit, DATA, DIR);
    printf("\t*DATA: %02X, *DIR: %02X\n", *DATA, *DIR);
    
    //PORT = DATA;
    //DDR = DIR;
    SDAbit = pdatabit;
    SCLbit = pclockbit;
    
    set(SDAbit);
    set(SCLbit);
    
    return;
}

I2C::~I2C()
{
    perror("I2C::~I2C()");
    return;    
}

unsigned char I2C::i2c_read(unsigned char slave_addr)
{
   perror("I2C::i2c_read()");

   unsigned char byte;
   
   i2c_start();
   i2c_tx(slave_addr | 1); //lower bit indicates read operation
   
   byte = i2c_rx(1); //assert a master ack
   i2c_stop();
   return byte;
}


void I2C::i2c_write(unsigned char slave_addr, unsigned char byte)
{
  // unsigned char byte;
  perror("I2C::i2c_write()");

  setoutput(SDAbit);
  setoutput(SCLbit);
  
   i2c_start();
  
   i2c_tx(slave_addr & 0xFE); //lower bit indicates \write operation
   
   i2c_tx(byte);
   
   i2c_stop();
   
   setinput(SDAbit);
   setinput(SCLbit);
   perror("\ti2c_write() finished");
   return;
}
void I2C::i2c_start()
{
   //START condition; SDA H to L while SCL = 1
  //Note: this is also a stop condition if SDA is low before SCL = 1!
  //Bus assumed idle when SDA and SCL are high?
    set(SDAbit);             // i2c start bit sequence
        usleep(SLEEP_INTERVAL);

    set(SCLbit);
        usleep(SLEEP_INTERVAL);

    clr(SDAbit);
        usleep(SLEEP_INTERVAL);

    clr(SCLbit);
}

void I2C::i2c_stop()
{
    //STOP condition; SDA from L to H while SCL = 1 
    clr(SDAbit);  
      usleep(SLEEP_INTERVAL);

    set(SCLbit); 
      usleep(SLEEP_INTERVAL);
    
    set(SDAbit); 
}

//shift out one byte MSB first
unsigned char I2C::i2c_tx(unsigned char byte) 
{   
  unsigned char i;
  static unsigned char ackbit; //static necessary?
  
  for(i = 0x80; i; i >>= 1)
  {
      if (byte & i) {
        set(SDAbit);
      }else{ 
        clr(SDAbit);
      }
       usleep(SLEEP_INTERVAL);
    set(SCLbit);
       usleep(SLEEP_INTERVAL);
    clr(SCLbit); //changing of SDA only allowed when SCL = 0
       usleep(SLEEP_INTERVAL);
  } 
    
    set(SDAbit);
       usleep(SLEEP_INTERVAL);
    
    setinput(SDAbit);//set SDA as input for ackbit
    
    set(SCLbit); //slave device now pulling SDA low for acknowledgement
       usleep(SLEEP_INTERVAL);

    ackbit = getbit(SDAbit);
     
    clr(SCLbit);
        usleep(SLEEP_INTERVAL);
        
    setoutput(SDAbit); //set SDA as ouput for further operations
    

    return ackbit;
}

unsigned char I2C::i2c_rx(unsigned char ack)
{
     unsigned char byte = 0, i;
     
   //  clr(SCLbit); //if SCL = 0 is not used in i2c_tx(after a tx, either RX, another tx, or stop is called)
     
  //   set(SDAbit);
     setinput(SDAbit);

     for (i = 0x80; i; i >>=1)
     {
       set(SCLbit);
       
       while(!getbit(SCLbit)); //wait till positive??
       
       if (getbit(SDAbit)) byte |= i; 
       clr(SCLbit);  
     }
     
     //Assert SDA(ack from master)
     setoutput(SDAbit);//SDA as output

    if(ack) clr(SDAbit); //assert SDA low while SCL is high?
    else set(SDAbit);
    
    set(SCLbit); //clock ack bit
    clr(SCLbit);
    
  //  setinput(SDAbit);

  //  set(SDAbit); //necessary??
     return byte;
}
